package com.shenda.device;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

import com.shenda.api.dto.DataDTO;
import com.shenda.api.entity.Data;
import com.shenda.device.enums.Cjms;
import com.shenda.device.enums.Status;
import com.shenda.device.message.ReqMsg;
import com.shenda.device.message.RespMsg_UploadStart;
import com.shenda.starter.config.Settings;
import com.swak.eventbus.EventBus;
import com.swak.rxtx.channel.Channel;
import com.swak.rxtx.codec.Encodes;
import com.swak.utils.IOUtils;
import com.swak.utils.Lists;
import com.swak.utils.StringUtils;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * 设备
 * 
 * @author lifeng
 */
@Getter
@Setter
@Accessors(chain = true)
public class Device implements Options, Encodes {

	// 设备相关
	private Channel channel;
	private String mac;
	private String cjms;
	private String cjpl;
	private String odr;
	private Status status = Status.断开;
	private volatile Upload upload;
	private volatile List<Upload> uploads = Collections.synchronizedList(new ArrayList<>());

	/**
	 * mac crc16
	 * 
	 * @return
	 */
	public String getGyroName() {
		return this.crc16(mac, true);
	}

	/**
	 * 添加设备
	 * 
	 * @param channel
	 * @return
	 */
	public Device connected(Channel channel) {
		this.channel = channel;
		this.status = channel.isActive() ? Status.在线 : Status.断开;
		return this;
	}

	/**
	 * 添加设备
	 * 
	 * @param channel
	 * @return
	 */
	public Device closed(Channel channel) {
		this.channel = channel;
		this.status = channel.isActive() ? Status.在线 : Status.断开;
		return this;
	}

	/**
	 * 重新连接
	 */
	@Override
	public void connect() {
		channel.connect();
		this.status = channel.isActive() ? Status.在线 : Status.断开;
	}

	/**
	 * 关闭连接
	 */
	@Override
	public void close() {
		channel.close();
	}

	/**
	 * 设置 -- 必须识别到了mac才能发送上传命令
	 */
	@Override
	public void startUpload() {
		if (StringUtils.isNotBlank(this.mac)) {
			Options.super.startUpload();
		}
	}

	/**
	 * 设置配置
	 */
	@Override
	public void deviceConfig(String cjms, String cjpl, String odr) {
		this.cjms = cjms;
		this.cjpl = cjpl;
		this.odr = odr;
		Options.super.deviceConfig(cjms, cjpl, odr);
	}

	/**
	 * 发送命令
	 */
	@Override
	public void sendCommand(ReqMsg command) {
		channel.write(command);
	}

	/**
	 * 上传的概况（服务线程在处理）
	 * 
	 * @return
	 */
	public List<DataDTO> uploadSurvey() {

		// 上传的数据
		List<DataDTO> datas = Lists.newArrayList();
		for (Upload upload : this.uploads) {
			datas.add(DataDTO.convert(upload.toData()));
		}

		// 删除已经入库的数据
		if (this.uploads.size() > 0) {

			// 是否全部处理完成
			boolean allOk = true;
			for (Upload upload : this.uploads) {
				if (!(upload.marked && upload.finished)) {
					allOk = false;
					break;
				}
			}

			// 统一删除已经处理好的
			if (allOk) {
				Iterator<Upload> iterator = this.uploads.iterator();
				while (iterator.hasNext()) {
					Upload upload = iterator.next();
					if (upload.marked && upload.finished) {
						iterator.remove();
					}
				}
			}
		}

		// 反转排序
		Collections.reverse(datas);
		return datas;
	}

	/**
	 * 开始上传(设备线程在处理)
	 */
	public void uploadStart(RespMsg_UploadStart head) {

		// 设置上传中的状态
		this.setStatus(Status.上传中);

		// 新设置保存点
		this.upload = new Upload().setStartTime(head.getStartTime()).setEndTime(head.getEndTime())
				.setCjms(head.getCjms()).setCjpl(head.getCjpl()).setOdr(head.getOdr()).setLength(head.getLength())
				.setFrames(head.getFrames());

		// 保存文件头
		this.upload.uploadHead(head.getData());

		// 保存上传点
		this.uploads.add(this.upload);
	}

	/**
	 * 上传数据(设备线程在处理)
	 * 
	 * @param data
	 */
	public void uploadData(byte[] data) {
		Upload upload = this.upload;
		if (upload != null && !upload.finished) {
			upload.uploadData(data);
			if (upload.finished) {
				EventBus.me().post(upload.toData());
				upload.marked = true;
			}
		}
	}

	/**
	 * 上传结束touch(设备线程在处理) -- 心跳处理
	 */
	public void uploadTouch() {

		// 保存检查点
		if (this.uploads.size() > 0) {
			Iterator<Upload> iterator = this.uploads.iterator();
			while (iterator.hasNext()) {
				Upload upload = iterator.next();
				if (!upload.marked && upload.finished) {
					EventBus.me().post(upload.toData());
					upload.marked = true;
				}
			}
		}

		// 设置上传成功
		if (this.uploads.size() == 0 && this.status == Status.上传中) {
			this.status = Status.在线;
			this.upload = null;
		}
	}

	/**
	 * 停止上传数据
	 */
	public void uploadStop() {

		// 执行一次清理数据
		this.uploadTouch();

		// 强制删除数据
		if (this.uploads.size() > 0) {
			this.uploads.clear();
		}

		// 执行一次清理数据
		this.uploadTouch();
	}

	/**
	 * 强制停止，并写入文件，导入的时候才用到
	 */
	public void forceStop() {

		// 保存检查点
		if (this.uploads.size() > 0) {
			Iterator<Upload> iterator = this.uploads.iterator();
			while (iterator.hasNext()) {
				Upload upload = iterator.next();
				EventBus.me().post(upload.toData());
			}
		}

		// 强制删除数据
		if (this.uploads.size() > 0) {
			this.uploads.clear();
		}
	}

	/**
	 * 一次上传 -- 数据上传处理
	 */
	@Getter
	@Setter
	@Accessors(chain = true)
	public class Upload {
		private String file;
		private String startTime;// 开始时间 14
		private String endTime;// 结束时间 14
		private Cjms cjms; // 采集模式 2
		private String cjpl;// 采集频率 4
		private String odr; // 采集ODR 4
		private int length; // 采集长度 8
		private int uploads; // 上传总数
		private int frames;// 帧数
		private BlockingQueue<byte[]> datas = new LinkedBlockingDeque<>();
		private boolean finished = false;
		private boolean marked = false;

		/**
		 * 转换为数据
		 * 
		 * @return
		 */
		public Data toData() {
			Data data = new Data();
			data.setName(file);
			data.setGyro(Device.this.mac);
			data.setStartTime(startTime);
			data.setEndTime(endTime);
			data.setCjms(cjms.getCmd());
			data.setCjpl(cjpl);
			data.setOdr(odr);
			data.setUploads(uploads);
			data.setTotal(length);
			data.setFrames(frames);
			data.setFinished(finished);
			data.setId(Data.id(Device.this.mac, startTime, endTime));
			return data.parseTimes();
		}

		/**
		 * 按顺序写入数据 -- 重新传递会覆盖数据
		 * 
		 * @param data
		 */
		public void uploadHead(byte[] data) {
			datas.add(data);
			this.write(false);
		}

		/**
		 * 按顺序写入数据
		 * 
		 * @param data
		 */
		public void uploadData(byte[] data) {
			datas.add(data);
			this.uploads += data.length;
			this.write(true);
			if (uploads >= length && datas.size() == 0) {
				this.finished = true;
			}
		}

		/**
		 * 写入数据
		 */
		private void write(boolean append) {

			// 创建设备目录
			File deviceFile = new File(Settings.me().getDataPath(), Device.this.getGyroName());
			if (!deviceFile.exists()) {
				deviceFile.mkdirs();
			}

			FileOutputStream out = null;
			try {
				this.file = new StringBuilder(startTime).append("_").append(cjms).append("_").append(cjpl).append("_")
						.append(odr).append(".dat").toString();
				File dataFile = new File(deviceFile, this.file);
				if (!dataFile.exists()) {
					dataFile.createNewFile();
				}
				out = new FileOutputStream(dataFile, append);
				while (true) {
					byte[] data = datas.poll();
					if (data == null) {
						break;
					}
					out.write(data);
				}
				out.flush();
			} catch (Exception e) {
			} finally {
				IOUtils.closeQuietly(out);
			}
		}
	}
}